#include "Defs.h"
#include "Rules.h"
#include "Utils.h"


Rules_T::Rules_T(const char * szDefaultLogFilesDir, const char * szConfigFile)
	:verbRule("VerbAllow", "VerbDeny", "Rules", szConfigFile)
	,httpVersionRule("ValidHttpVersion", "Rules", szConfigFile)
	,extensionRule("ExtensionAllow", "ExtensionDeny", "Rules", szConfigFile)
	,hostPortRule("ValidHostPort", "Rules", szConfigFile)
	,headerNameRule("DenyHeaderName", "Rules", szConfigFile)
	,urlSequenceRule("DenyUrlSequence", "Rules", szConfigFile)
	,querySequenceRule("DenyQuerySequence", "Rules", szConfigFile)
	,maxLenByHeaderRule("ValidMaxLenByHeader", "Rules", szConfigFile)
	,allowDotInUrlRule("AllowDotInUrl", 0, "Rules", szConfigFile)
	,allowHighBitInUrlRule("AllowHighBitInUrl", 0, "Rules", szConfigFile)
	,allowHighBitInQueryRule("AllowHighBitInQuery", 0, "Rules", szConfigFile)
	,allowHighBitInPayloadRule("AllowHighBitInPayload", 0, "Rules", szConfigFile)
	,canonUrlBeforeScanRule("CanonUrlBeforeScan", 1, "Rules", szConfigFile)
	,canonQueryBeforeScanRule("CanonQueryBeforeScan", 1, "Rules", szConfigFile)
	,failOnReCanonUrlChangeRule("FailOnReCanonUrlChange", 1, "Rules", szConfigFile)
	,failOnReCanonQueryChangeRule("FailOnReCanonQueryChange", 1, "Rules", szConfigFile)
	,removeBannerRule("RemoveBanner", 0, "Rules", szConfigFile)
	,urlMaxLenRule("UrlMaxLen", "Rules", szConfigFile)
	,queryMaxLenRule("QueryMaxLen", "Rules", szConfigFile)
	,headerNameMaxLenRule("HeaderNameMaxLen", "Rules", szConfigFile)
	,headerValueMaxLenRule("HeaderValueMaxLen", "Rules", szConfigFile)
	,payloadMaxLenRule("PayloadMaxLen", "Rules", szConfigFile)
	,contentLengthMaxLenRule("ContentLengthMaxLen", "Rules", szConfigFile)
{
	char szData[MAX_PATH];

	//
	// save the config file name
	//

	FillMemory(_szConfigFile, sizeof(_szConfigFile), 0);
	_snprintf(_szConfigFile, sizeof(_szConfigFile) - 1, szConfigFile);

	// 
	// Get Name option
	//

	FillMemory(szData, sizeof(szData), 0);
	GetPrivateProfileString("Rules", "Name", "",
		szData, 
		sizeof(szData) - 1, 
		szConfigFile);

	if( lstrlen(szData) == 0 )
	{
		unsigned int startNameIndex = String_T(szConfigFile).LastIndexOf("\\") + 1;
		unsigned int endNameIndex = String_T(szConfigFile).LastIndexOf(".");
		RtlMoveMemory(szData, szConfigFile + startNameIndex, endNameIndex - startNameIndex);
	}

	FillMemory(szName, sizeof(szName), 0);
	_snprintf(szName, sizeof(szName) - 1, szData);
	
	// 
	// Get ChangeBanner option
	//
	
	if( !removeBannerRule.flag )
	{
		FillMemory(szData, sizeof(szData), 0);
		GetPrivateProfileString("Rules", "ChangeBanner", "", 
			szData, sizeof(szData) - 1, szConfigFile);

		if( lstrlen(szData) > 0 )
		{
			serverBanner.Concat(szData);
		}
	}

	// 
	// Get RejectPage option
	//

	FillMemory(szRejectFilename, sizeof(szRejectFilename), 0);
	GetPrivateProfileString("Rules", "RejectPage", "", 
		szRejectFilename, sizeof(szRejectFilename) - 1, szConfigFile);

	if( lstrlen(szRejectFilename) > 0 )
	{
		Utils_T::GetFileContents(szRejectFilename, rejectPageContent);
	}

	// 
	// Get LogFilesDir option
	// Remove trailing backslash
	//
	
	FillMemory(szLogFilesDir, sizeof(szLogFilesDir), 0);
	GetPrivateProfileString("Rules", "LogFilesDir", szDefaultLogFilesDir, 
		szLogFilesDir, sizeof(szLogFilesDir) - 1, szConfigFile);

	if( szLogFilesDir[lstrlen(szLogFilesDir) - 1] == '\\' )
	{
		szLogFilesDir[lstrlen(szLogFilesDir) - 1] = '\0';
	}
	

	char szLogFilename[MAX_PATH];
	FillMemory(szLogFilename, sizeof(szLogFilename), 0);
	_snprintf(szLogFilename, sizeof(szLogFilename) - 1, "%s\\%s",
		szLogFilesDir, szName);

	logPtr = Log_T::Load(szLogFilename);

	//
	// Dump the configuration options
	//

	char szBuffer[8192];
	FillMemory(szBuffer, sizeof(szBuffer), 0);

	unsigned int bufferSize = sizeof(szBuffer) - 1;
	unsigned int bufferCursor = 0;

	_snprintf(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor, 
		"# ================ Rules <%s>\r\n", szName);
	bufferCursor = lstrlen(szBuffer);

	// 
	// Dump Verbs Allow/Deny option and list
	//

	verbRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump HttpVersion option and list
	//

	httpVersionRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump Extensions Allow/Deny option and list
	//

	extensionRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump HostPort option and list
	//

	hostPortRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump HeaderName option and list
	//

	headerNameRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump MaxLenByHeader option and list
	//

	maxLenByHeaderRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump UrlSequence option and list
	//

	urlSequenceRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump QuerySequence option and list
	//

	querySequenceRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump AllowDotInUrl option
	//

	allowDotInUrlRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);

	// 
	// Dump AllowHighBitInUrl option
	//

	allowHighBitInUrlRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump CanonUrlBeforeScan and FailOnReCanonUrlChange options
	//

	canonUrlBeforeScanRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);

	failOnReCanonUrlChangeRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump AllowHighBitInQuery option
	//

	allowHighBitInQueryRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);

	// 
	// Dump CanonQueryBeforeScan and FailOnReCanonQueryChange options
	//

	canonQueryBeforeScanRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);

	failOnReCanonQueryChangeRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump UrlMaxLen option
	//

	urlMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump QueryMaxLen option
	//

	queryMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump HeaderNameMaxLen option
	//

	headerNameMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump HeaderValueMaxLen option
	//

	headerValueMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump PayloadMaxLen option
	//

	payloadMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump ContentLengthMaxLen option
	//

	contentLengthMaxLenRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	// 
	// Dump AllowHighBitInPayload option
	//

	allowHighBitInPayloadRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);

	// 
	// Dump RemoveBanner and ChangeBanner option
	//

	removeBannerRule.ToString(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor);
	bufferCursor = lstrlen(szBuffer);
	
	if( serverBanner.Length() > 0 )
	{
		_snprintf(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor, 
			"# ChangeBanner=%s\r\n", serverBanner.Ptr());
		bufferCursor = lstrlen(szBuffer);
	}

	// 
	// Dump RejectPage option
	//
	
	if( lstrlen(szRejectFilename) > 0 )
	{
		_snprintf(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor, 
			"# RejectPage=%s\r\n", szRejectFilename);
		bufferCursor = lstrlen(szBuffer);
	}

	// 
	// Dump LogFilesDir option
	//
	
	_snprintf(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor, 
		"# LogFilesDir=%s\r\n", szLogFilesDir);
	bufferCursor = lstrlen(szBuffer);

	// 
	// end
	//
	
	_snprintf(szBuffer + bufferCursor, bufferSize - 1 - bufferCursor, 
		"# ================ end\r\n");
	bufferCursor = lstrlen(szBuffer);

	//
	// write to log
	// 

	logPtr->Write(szBuffer);
}

void Rules_T::Log(const Session_T & session, Request_T & request, const Error_T & err)
{
	logPtr->Write(session, request, szName, err);
}

Rules_T::~Rules_T()
{
	logPtr->Write("# ================ Shutdown\r\n");
}

void Rules_T::CrackUrl(const Buffer_T & url, Buffer_T & urlPath, Buffer_T & urlFile, Buffer_T & urlExt)
{
	// check for the last slash. Assume the 1st char is a slash
	unsigned int ix = url.LastIndexOf("/");

	// jump over the last slash and set the path
	urlPath = url.SubBuffer(0, ++ix);

	// check if there is a file (+ extension)
	if( url.Length() == ix )
	{
		return;
	}

	unsigned int iy = url.IndexOf(".", ix);
	if( iy == url.Length() )
	{
		urlFile = url.SubBuffer(ix);
		return;
	}

	urlFile = url.SubBuffer(ix, iy - ix);
	urlExt = url.SubBuffer(iy);
}

//
// Check for an (un)allowed verb
//
Error_T::Code_T Rules_T::FilterVerb(const Buffer_T & verb)
{
	if( verbRule.flag )
	{
		if( verbRule.tokenList.IndexOf(verb, true) == verbRule.tokenList.Count() )
		{
			return Error_T::ERR_VERB_ALLOWED;
		}
	}

	else if( verbRule.tokenList.IndexOf(verb, true) != verbRule.tokenList.Count() )
	{
		return Error_T::ERR_VERB_DENIED;
	}

	return Error_T::ERR_NONE;
}

Error_T::Code_T Rules_T::FilterSchema(const Buffer_T & schema)
{
	if( (schema.Length() == 0) || !hostPortRule.flag )
	{
		return Error_T::ERR_NONE;
	}

	// Skip the ASCII_AT (userinfo)
	unsigned int ix = schema.IndexOf("@");
	if( ix != schema.Length() )
	{
		if( hostPortRule.tokenList.IndexOf(schema.SubBuffer(ix + 1), false) == hostPortRule.tokenList.Count() )
		{
			return Error_T::ERR_SCHEMA_FORMAT;
		}
	}
	else if( hostPortRule.tokenList.IndexOf(schema, false) == hostPortRule.tokenList.Count() )
	{
		return Error_T::ERR_SCHEMA_FORMAT;
	}

	return Error_T::ERR_NONE;
}

Error_T::Code_T Rules_T::FilterUrl(Buffer_T & url)
{
	// 
	// If url exceeds allowed size then fail
	//
	
	if( urlMaxLenRule.flag && (url.Length() > urlMaxLenRule.len) )
	{
		return Error_T::ERR_URL_MAXLEN;
	}

	// 
	// if url does not start with / or * then fail
	//
	
	if( (url.Length() == 0) ||
		((url.Length() == 1) && (url[0] != '*') && (url[0] != '/')) ||
		((url.Length() > 1) && (url[0] != '/')) )
	{
		return Error_T::ERR_URL_FIRSTBYTE;
	}

	if( url[0] == '*' )
	{
		return Error_T::ERR_NONE;
	}

	// 
	// If canonicalization must occur
	//
	
	if( canonUrlBeforeScanRule.flag )
	{
		// Save size
		unsigned int len = url.Length();
		url.Decode();

		// If re-canonicalization must occur
		if( (len != url.Length()) && failOnReCanonUrlChangeRule.flag )
		{
			// Save size of previous canon
			len = url.Length();
			url.Decode();

			// If canon is not equal to previous canon then fail
			if( len != url.Length() )
			{
				return Error_T::ERR_URL_CANON_CHANGE;
			}
		}
	}

	//
	// Split the Url
	//
	
	Buffer_T urlPath, urlFile, urlExt;
	CrackUrl(url, urlPath, urlFile, urlExt);

	// 
	// Do not allow ASCII_DOT in the path
	//
	
	if( !allowDotInUrlRule.flag && (urlPath.IndexOf(".") != urlPath.Length()) )
	{
		return Error_T::ERR_URL_DOT_NOEXT;
	}

	// 
	// Check for (un)allowed extensions
	//
	
	if( urlExt.Length() > 0 )
	{
		if( extensionRule.flag )
		{
			if( extensionRule.tokenList.IndexOf(urlExt, false) == extensionRule.tokenList.Count() )
			{
				return Error_T::ERR_URL_EXTALLOWED;
			}
		}

		else if( extensionRule.tokenList.IndexOf(urlExt, false) != extensionRule.tokenList.Count() )
		{
			return Error_T::ERR_URL_EXTDENIED;
		}
	}

	// 
	// If high bit character or ctl found then fail
	//
	
	for( unsigned int ix = 0 ; ix < url.Length() ; ix++ )
	{
		if( !allowHighBitInUrlRule.flag && ((url[ix] & 0x80) == 0x80) )
		{
			return Error_T::ERR_URL_HIGHBIT;
		}

		if( url[ix] < 0x20 )
		{
			return Error_T::ERR_URL_CTL;
		}
	}

	// 
	// If invalid sequence found then fail
	//
	
	if( urlSequenceRule.flag )
	{
		for( unsigned int ix = 0 ; ix < urlSequenceRule.tokenList.Count() ; ix++ )
		{
			if( url.IndexOf(*urlSequenceRule.tokenList[ix]) != url.Length() )
			{
				return Error_T::ERR_URL_SEQ;
			}
		}
	}

	return Error_T::ERR_NONE;
}

Error_T::Code_T Rules_T::FilterQuery(Buffer_T & query)
{
	// 
	// If query exceeds allowed size then fail
	//

	if( queryMaxLenRule.flag && (query.Length() > queryMaxLenRule.len) )
	{
		return Error_T::ERR_QUERY_MAXLEN;
	}

	// 
	// If canonicalization must occur
	//

	if( canonQueryBeforeScanRule.flag )
	{
		// Save size
		unsigned int len = query.Length();
		query.Decode();

		// If re-canonicalization must occur
		if( (len != query.Length()) && failOnReCanonQueryChangeRule.flag )
		{
			// Save size of previous canon
			len = query.Length();
			query.Decode();

			// If canon is not equal to previous canon then fail
			if( len != query.Length() )
			{
				return Error_T::ERR_URL_CANON_CHANGE;
			}
		}
	}

	// 
	// If high bit character or ctl found then fail
	//

	for( unsigned int ix = 0 ; ix < query.Length() ; ix++ )
	{
		if( !allowHighBitInQueryRule.flag && ((query[ix] & 0x80) == 0x80) )
		{
			return Error_T::ERR_QUERY_HIGHBIT;
		}

		if( (query[ix] < 0x20) && (query[ix] != 0x09) && (query[ix] != 0x0A) && (query[ix] != 0x0D) )
		{
			return Error_T::ERR_QUERY_CTL;
		}
	}

	// 
	// If invalid sequence found then fail
	//

	if( querySequenceRule.flag )
	{
		for( unsigned int ix = 0 ; ix < querySequenceRule.tokenList.Count() ; ix++ )
		{
			if( query.IndexOf(*querySequenceRule.tokenList[ix]) != query.Length() )
			{
				return Error_T::ERR_QUERY_SEQ;
			}
		}
	}

	return Error_T::ERR_NONE;
}

// 
// Check for an allowed http version
//

Error_T::Code_T Rules_T::FilterVersion(const Buffer_T & version)
{
	if( httpVersionRule.flag && (httpVersionRule.tokenList.IndexOf(version, true) == httpVersionRule.tokenList.Count()) )
	{
		return Error_T::ERR_VERSION_FORMAT;
	}

	return Error_T::ERR_NONE;
}

Error_T::Code_T Rules_T::FilterHeaderName(const Buffer_T & headerName)
{
	// 
	// If the header name exceeds allowed size then fail
	//

	if( headerNameMaxLenRule.flag && (headerName.Length() > headerNameMaxLenRule.len) )
	{
		return Error_T::ERR_HEADERNAME_MAXLEN;
	}

	// 
	// Check for allowed HeaderName
	//

	if( headerNameRule.flag && (headerNameRule.tokenList.IndexOf(headerName, false) != headerNameRule.tokenList.Count()) )
	{
		return Error_T::ERR_HEADER_FORMAT;
	}

	// 
	// Check if header Name has unallowed byte
	//

	for( unsigned int ix = 0 ; ix < headerName.Length() ; ix++ )
	{
		if( !((headerName[ix] > 0x20)  &&
			  (headerName[ix] < 0x7F)  &&
			  (headerName[ix] != '(')  &&
			  (headerName[ix] != ')')  &&
			  (headerName[ix] != '<')  &&
			  (headerName[ix] != '>')  &&
			  (headerName[ix] != '@')  &&
			  (headerName[ix] != ',')  &&
			  (headerName[ix] != ';')  &&
			  (headerName[ix] != ':')  &&
			  (headerName[ix] != '\\') &&
			  (headerName[ix] != '\"') &&
			  (headerName[ix] != '/')  &&
			  (headerName[ix] != '[')  &&
			  (headerName[ix] != ']')  &&
			  (headerName[ix] != '?')  &&
			  (headerName[ix] != '=')  &&
			  (headerName[ix] != '{')  &&
			  (headerName[ix] != '}')) )
		{
			return Error_T::ERR_HEADERNAME_FORMAT;
		}
	}

	return Error_T::ERR_NONE;
}

Error_T::Code_T Rules_T::FilterHeaderValue(const Buffer_T & headerName, const Buffer_T & headerValue)
{
	bool isQuotedPair, hasSlash;

	// 
	// If the header value exceeds allowed size then fail
	//
	
	if( headerValueMaxLenRule.flag && (headerValue.Length() > headerValueMaxLenRule.len) )
	{
		return Error_T::ERR_HEADERVALUE_MAXLEN;
	}

	// 
	// Strip leading and trailing spaces from header value
	// (RFC based)
	//

	unsigned int ix, i1, i2;
	int iy;

	for( ix = 0 ; (ix < headerValue.Length()) && (headerValue[ix] == ' ') ; ix++ );
	if( ix == headerValue.Length() )
	{
		i1 = i2 = 0;
	}
	else
	{
		i1 = ix;
		for( iy = headerValue.Length() - 1 ; (iy >= 0) && (headerValue[iy] == ' ') ; iy-- );
		i2 = iy + 1;
	}

	Buffer_T headerValueStripped = headerValue.SubBuffer(i1, i2 - i1);

	// 
	// Check if header Value has unallowed byte
	//
	hasSlash = false;
	isQuotedPair = false;
	for( ix = 0 ; ix < headerValueStripped.Length() ; ix++ )
	{
		if( headerValueStripped[ix] == '\"' )
		{
			isQuotedPair = !isQuotedPair;
		}

		else if( isQuotedPair )
		{
			if( !((headerValueStripped[ix] == 0x09) || ((headerValueStripped[ix] >= 0x20) && (headerValueStripped[ix] < 0x7F))) )
			{
				if( !hasSlash )
				{
					if( headerValueStripped[ix] != '\\' )
						break;
					hasSlash = true;
				}
				else if( (headerValueStripped[ix] & 0x80) == 0x80 )
					break;
				else
					hasSlash = false;
			}
			else
				hasSlash = headerValueStripped[ix] == '\\';
		}
			
		else if( !((headerValueStripped[ix] == 0x09) || ((headerValueStripped[ix] >= 0x20) && (headerValueStripped[ix] < 0x7F))) )
			break;
	}

	if( (ix < headerValueStripped.Length()) || isQuotedPair )
	{
		return Error_T::ERR_HEADERVALUE_FORMAT;
	}

	// 
	// If HeaderName is 'Host' then validate Host:Port
	//
	
	if( hostPortRule.flag && headerName.Equals("Host", false) )
	{
		if( hostPortRule.tokenList.IndexOf(headerValueStripped, false) == hostPortRule.tokenList.Count() )
		{
			return Error_T::ERR_HEADER_HOST;
		}
	}

	// 
	// If HeaderName is 'Content-Length' then validate
	// that is only made by digits.
	// lstrlen("4294967295") = 10
	//
	
	if( headerName.Equals("Content-Length", false) )
	{
		if( (headerValueStripped.Length() == 0) || (headerValueStripped.Length() > 10) || !headerValueStripped.IsNumeric() )
		{
			return Error_T::ERR_HEADER_CONTENTLENGTH;
		}

		// 
		// Validate Content-Length maximum value
		//
		if( contentLengthMaxLenRule.flag && (headerValueStripped.ToNumber() > contentLengthMaxLenRule.len) )
		{
			return Error_T::ERR_CONTENTHEADER_MAXVALUE;
		}
	}

	// 
	// Check maximum length of header value by header name
	//
	if( maxLenByHeaderRule.flag )
	{
		ix = maxLenByHeaderRule.tokenList.IndexOf(headerName, false);
		if( (ix != maxLenByHeaderRule.tokenList.Count()) && 
			(headerValueStripped.Length() > *maxLenByHeaderRule.numList[ix]) )
		{
			return Error_T::ERR_MAXLENBYHEADER;
		}
	}

	return Error_T::ERR_NONE;
}

//
// Filter http headers
//
Error_T::Code_T Rules_T::FilterHeaders(Request_T & request)
{
	Error_T::Code_T code = Error_T::ERR_WINDOWS;

	// 
	// HTTP Verb validation step
	//

	code = FilterVerb(request.Verb());
	if( Error_T::ERR_NONE != code )
	{
		return code;
	}

	// 
	// If invalid schema then fail
	//
	
	code = FilterSchema(request.Schema());
	if( Error_T::ERR_NONE != code )
	{
		return code;
	}

	// 
	// Check for an allowed url
	//
	
	code = FilterUrl(request.Url());
	if( Error_T::ERR_NONE != code )
	{
		return code;
	}

	// 
	// Check for an allowed query
	//
	
	code = FilterQuery(request.Query());
	if( Error_T::ERR_NONE != code )
	{
		return code;
	}

	// 
	// Check for an allowed http version
	//
	
	code = FilterVersion(request.Version());
	if( Error_T::ERR_NONE != code )
	{
		return code;
	}

	// 
	// validate http request headers
	//
	
	for( unsigned int ix = 0 ; ix < request.Headers().Count() ; ix++ )
	{
		// 
		// Check for allowed http header name
		//
		
		code = FilterHeaderName(request.Headers()[ix]->name);
		if( Error_T::ERR_NONE != code )
		{
			return code;
		}

		// 
		// Check for allowed http header name/value
		//
		
		code = FilterHeaderValue(request.Headers()[ix]->name, request.Headers()[ix]->value);
		if( Error_T::ERR_NONE != code )
		{
			return code;
		}
	}

	return Error_T::ERR_NONE;
}

// 
// Validate (if configured) the payload size
//

Error_T::Code_T Rules_T::FilterEntitySize(unsigned int entitySize)
{
	if( payloadMaxLenRule.flag && (entitySize > payloadMaxLenRule.len) )
	{
		return Error_T::ERR_PAYLOAD_MAXLEN;
	}

	return Error_T::ERR_NONE;
}

// 
// If high bit character found in Entity then fail.
//

Error_T::Code_T Rules_T::FilterEntityData(const Buffer_T & entityData)
{
	if( !allowHighBitInPayloadRule.flag )
	{
		for( unsigned int ix = 0 ; ix < entityData.Length() ; ix++ )
		{
			if( (entityData[ix] & 0x80) == 0x80 )
			{
				return Error_T::ERR_PAYLOAD_HIGHBIT;
			}
		}
	}

	return Error_T::ERR_NONE;
}

//
// Static Members
//

List_T<Rules_T> * Rules_T::s_rulesListPtr = 0;

void Rules_T::Load()
{
	s_rulesListPtr = new List_T<Rules_T>();
}

Rules_T * Rules_T::Load(const char * szDefaultLogFilesDir, const char * szConfigFile)
{
	for( unsigned int ix = 0 ; ix < s_rulesListPtr->Count() ; ix++ )
	{
		if( String_T(s_rulesListPtr->operator[](ix)->_szConfigFile).Equals(szConfigFile, false) )
		{
			return s_rulesListPtr->operator[](ix);
		}
	}

	return s_rulesListPtr->Add(new Rules_T(szDefaultLogFilesDir, szConfigFile));
}

void Rules_T::Unload()
{
	if( s_rulesListPtr )
	{
		delete s_rulesListPtr;
	}
}